package com.ssm.basis.web.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ssm.basis.model.pojo.Result;
import com.ssm.basis.service.exception.CustomException;
import com.ssm.basis.service.util.SsmContextHolder;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMappingJacksonResponseBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import static com.ssm.basis.configuration.ConfigurationPackage.ROOT_PACKAGE;
import static com.ssm.basis.model.pojo.Result.*;
import static com.ssm.basis.service.constant.SecurityConstant.SUPPRESS_WARNING_ALL;
import static java.util.Objects.nonNull;

/**
 * 统一返回结构
 * https://felord.cn/spring-mvc-restbody.html
 *
 * @see Result
 * @see RequestResponseBodyMethodProcessor
 * @see AbstractMappingJacksonResponseBodyAdvice
 */
@RequiredArgsConstructor
@ControllerAdvice(ROOT_PACKAGE)
class RestResultWrapper implements ResponseBodyAdvice<Object> {
    final ObjectMapper objectMapper;

    @Override
    @SuppressWarnings(SUPPRESS_WARNING_ALL)
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // returnType.getMethod().getDeclaringClass().getName().startsWith("com.ssm")
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class) ||
                MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
    }

    @Override
    @SuppressWarnings(SUPPRESS_WARNING_ALL)
    public Object beforeBodyWrite(Object body, @Nullable MethodParameter returnType, MediaType selectedContentType,
                                  @Nullable Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  @Nullable ServerHttpRequest request, @Nullable ServerHttpResponse response) {
        if (body instanceof String) {
            // String 内容的 Content-Type 默认为 text/plain;charset=UTF-8，这里修改响应头为 JSON
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            try {
                // handler 的返回类型是 String，保证一致性，这里需要转回 String 类型
                return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(Result.ok(body));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                throw new CustomException(e);
            }
        }

        if (body instanceof Throwable) {
            String error = body.getClass().getName();
            String message = ((Throwable) body).getLocalizedMessage();
            message = nonNull(message) ? message : ERROR_REASON_PHRASE;
            String code = body instanceof CustomException ? ((CustomException) body).getCode() : ERROR;
            return Result.error().setCode(code).setMessage(message).setError(error);
        }

        if (body instanceof Result) {
            return body;
        }

        String message = nonNull(SsmContextHolder.getMessage()) ? SsmContextHolder.getMessage() : OK_REASON_PHRASE;
        return Result.ok(body).setMessage(message);
    }
}
